/****************************************************************************
*
*    The MIT License (MIT)
*
*    Copyright (c) 2014 - 2023 Vivante Corporation
*
*    Permission is hereby granted, free of charge, to any person obtaining a
*    copy of this software and associated documentation files (the "Software"),
*    to deal in the Software without restriction, including without limitation
*    the rights to use, copy, modify, merge, publish, distribute, sublicense,
*    and/or sell copies of the Software, and to permit persons to whom the
*    Software is furnished to do so, subject to the following conditions:
*
*    The above copyright notice and this permission notice shall be included in
*    all copies or substantial portions of the Software.
*
*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
*    DEALINGS IN THE SOFTWARE.
*
*****************************************************************************
*
*    The GPL License (GPL)
*
*    Copyright (C) 2014 - 2023 Vivante Corporation
*
*    This program is free software; you can redistribute it and/or
*    modify it under the terms of the GNU General Public License
*    as published by the Free Software Foundation; either version 2
*    of the License, or (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program; if not, write to the Free Software Foundation,
*    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*****************************************************************************
*
*    Note: This software is released under dual MIT and GPL licenses. A
*    recipient may use this file under the terms of either the MIT license or
*    GPL License. If you wish to use only one license not the other, you can
*    indicate your decision by deleting one of the above license notices in your
*    version of this file.
*
*****************************************************************************/

#include "gc_hal_kernel_precomp.h"

#if gcdENABLE_VG

#    include "gc_hal_kernel_hardware_command_vg.h"

#    define _GC_OBJ_ZONE        gcvZONE_COMMAND

#ifdef __QNXNTO__
extern gceSTATUS
drv_signal_mgr_add(gctUINT32 Pid, gctINT32 Rcvid, const struct sigevent *Event,
                   gctUINT64 Signal, gctPOINTER *Handle);
#    endif

/******************************************************************************
 *********************************** Debugging ********************************
 ******************************************************************************/

#    define gcvDISABLE_TIMEOUT     1
#    define gcvDUMP_COMMAND_BUFFER 0
#    define gcvDUMP_COMMAND_LINES  0

#if gcvDEBUG || defined(EMULATOR) || gcvDISABLE_TIMEOUT
#        define gcvQUEUE_TIMEOUT ~0
#    else
#        define gcvQUEUE_TIMEOUT 10
#    endif

/******************************************************************************
 ********************************** Definitions *******************************
 ******************************************************************************/

/* Minimum buffer size. */
#    define gcvMINUMUM_BUFFER                                                           \
        (gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) +                                            \
         gcmSIZEOF(gcsKERNEL_CMDQUEUE) * 2)

#    define gcmDECLARE_INTERRUPT_HANDLER(Block, Number)                                 \
        static gceSTATUS                                                                \
        _EventHandler_##Block##_##Number(IN gckVGKERNEL Kernel)

#define gcmDEFINE_INTERRUPT_HANDLER(Block, Number) \
    gcmDECLARE_INTERRUPT_HANDLER(Block, Number) \
    { \
        return _EventHandler_Block( \
            Kernel, \
            &Kernel->command->taskTable[gcvBLOCK_##Block], \
            gcvFALSE \
            ); \
    }

#define gcmDEFINE_INTERRUPT_HANDLER_ENTRY(Block, Number) \
    { gcvBLOCK_##Block, _EventHandler_##Block##_##Number }

/* Block interrupt handling table entry. */
typedef struct _gcsBLOCK_INTERRUPT_HANDLER *gcsBLOCK_INTERRUPT_HANDLER_PTR;
typedef struct _gcsBLOCK_INTERRUPT_HANDLER {
    gceBLOCK             block;
    gctINTERRUPT_HANDLER handler;
} gcsBLOCK_INTERRUPT_HANDLER;

/* Queue control functions. */
typedef struct _gcsQUEUE_UPDATE_CONTROL *gcsQUEUE_UPDATE_CONTROL_PTR;
typedef struct _gcsQUEUE_UPDATE_CONTROL {
    gctOBJECT_HANDLER execute;
    gctOBJECT_HANDLER update;
    gctOBJECT_HANDLER lastExecute;
    gctOBJECT_HANDLER lastUpdate;
} gcsQUEUE_UPDATE_CONTROL;

/******************************************************************************
 ********************************* Support Code *******************************
 ******************************************************************************/
static gceSTATUS
_FlushMMU(IN gckVGCOMMAND Command)
{
    gceSTATUS     status;
    gctUINT32     oldValue;
    gckVGHARDWARE hardware = Command->hardware;

    gcmkONERROR(gckOS_AtomicExchange(Command->os, hardware->pageTableDirty, 0, &oldValue));

    if (oldValue) {
        /* Page Table is upated, flush mmu before commit. */
        gcmkONERROR(gckVGHARDWARE_FlushMMU(hardware));
    }

    return gcvSTATUS_OK;
OnError:
    return status;
}

static gceSTATUS
_WaitForIdle(IN gckVGCOMMAND Command, IN gcsKERNEL_QUEUE_HEADER_PTR Queue)
{
    gceSTATUS status = gcvSTATUS_OK;
    gctUINT32 idle;
    gctUINT   timeout = 0;

    /* Loop while not idle. */
    while (Queue->pending) {
        /* Did we reach the timeout limit? */
        if (timeout == gcvQUEUE_TIMEOUT) {
            /* Hardware is probably dead... */
            return gcvSTATUS_TIMEOUT;
        }

        /* Sleep for 100ms. */
        gcmkERR_BREAK(gckOS_Delay(Command->os, 100));

        /* Not the first loop? */
        if (timeout > 0) {
            /* Read IDLE register. */
            gcmkVERIFY_OK(gckVGHARDWARE_GetIdle(Command->hardware, &idle));

            gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_COMMAND,
                           "%s: timeout, IDLE=%08X\n",
                           __FUNCTION__, idle);
        }

        /* Increment the timeout counter. */
        timeout += 1;
    }

    /* Return status. */
    return status;
}

static gctINT32
_GetNextInterrupt(IN gckVGCOMMAND Command, IN gceBLOCK Block)
{
    gctUINT                 index;
    gcsBLOCK_TASK_ENTRY_PTR entry;
    gctINT32                interrupt;

    /* Get the block entry. */
    entry = &Command->taskTable[Block];

    /* Make sure we have initialized interrupts. */
    gcmkASSERT(entry->interruptCount > 0);

    /* Decrement the interrupt usage semaphore. */
    gcmkVERIFY_OK(gckOS_DecrementSemaphore(Command->os, entry->interruptSemaphore));

    /* Get the value index. */
    index = entry->interruptIndex;

    /* Get the interrupt value. */
    interrupt = entry->interruptArray[index];

    /* Must be a valid value. */
    gcmkASSERT((interrupt >= 0) && (interrupt <= 31));

    /* Advance the index to the next value. */
    index += 1;

    /* Set the new index. */
    entry->interruptIndex = (index == entry->interruptCount) ? 0 : index;

    /* Return interrupt value. */
    return interrupt;
}

/******************************************************************************
 ***************************** Task Storage Management ************************
 ******************************************************************************/

/* Minimum task buffer size. */
#    define gcvMIN_TASK_BUFFER (gcmSIZEOF(gcsTASK_CONTAINER) + 128)

/* Free list terminator. */
#    define gcvFREE_TASK_TERMINATOR ((gcsTASK_CONTAINER_PTR)gcmINT2PTR(~0))

/*----------------------------------------------------------------------------*/
/*------------------- Allocated Task Buffer List Management ------------------*/

static void
_InsertTaskBuffer(IN gcsTASK_CONTAINER_PTR AddAfter, IN gcsTASK_CONTAINER_PTR Buffer)
{
    gcsTASK_CONTAINER_PTR addBefore;

    /* Cannot add before the first buffer. */
    gcmkASSERT(AddAfter != gcvNULL);

    /* Create a shortcut to the next buffer. */
    addBefore = AddAfter->allocNext;

    /* Initialize the links. */
    Buffer->allocPrev = AddAfter;
    Buffer->allocNext = addBefore;

    /* Link to the previous buffer. */
    AddAfter->allocNext = Buffer;

    /* Link to the next buffer. */
    if (addBefore != gcvNULL)
        addBefore->allocPrev = Buffer;
}

static void
_RemoveTaskBuffer(IN gcsTASK_CONTAINER_PTR Buffer)
{
    gcsTASK_CONTAINER_PTR prev;
    gcsTASK_CONTAINER_PTR next;

    /* Cannot remove the first buffer. */
    gcmkASSERT(Buffer->allocPrev != gcvNULL);

    /* Create shortcuts to the previous and next buffers. */
    prev = Buffer->allocPrev;
    next = Buffer->allocNext;

    /* Tail buffer? */
    if (next == gcvNULL) {
        /* Remove from the list. */
        prev->allocNext = gcvNULL;
    } else { /* Buffer from the middle. */
        prev->allocNext = next;
        next->allocPrev = prev;
    }
}

/*----------------------------------------------------------------------------*/
/*--------------------- Free Task Buffer List Management ---------------------*/

static void
_AppendToFreeList(IN gckVGCOMMAND Command, IN gcsTASK_CONTAINER_PTR Buffer)
{
    /* Cannot be a part of the free list already. */
    gcmkASSERT(Buffer->freePrev == gcvNULL);
    gcmkASSERT(Buffer->freeNext == gcvNULL);

    /* First buffer to add? */
    if (Command->taskFreeHead == gcvNULL) {
        /* Terminate the links. */
        Buffer->freePrev = gcvFREE_TASK_TERMINATOR;
        Buffer->freeNext = gcvFREE_TASK_TERMINATOR;

        /* Initialize the list pointer. */
        Command->taskFreeHead = Buffer;
        Command->taskFreeTail = Buffer;
    } else { /* Not the first, add after the tail. */
        /* Initialize the new tail buffer. */
        Buffer->freePrev = Command->taskFreeTail;
        Buffer->freeNext = gcvFREE_TASK_TERMINATOR;

        /* Add after the tail. */
        Command->taskFreeTail->freeNext = Buffer;
        Command->taskFreeTail           = Buffer;
    }
}

static void
_RemoveFromFreeList(IN gckVGCOMMAND Command, IN gcsTASK_CONTAINER_PTR Buffer)
{
    /* Has to be a part of the free list. */
    gcmkASSERT(Buffer->freePrev != gcvNULL);
    gcmkASSERT(Buffer->freeNext != gcvNULL);

    /* Head buffer? */
    if (Buffer->freePrev == gcvFREE_TASK_TERMINATOR) {
        /* Tail buffer as well? */
        if (Buffer->freeNext == gcvFREE_TASK_TERMINATOR) {
            /* Reset the list pointer. */
            Command->taskFreeHead = gcvNULL;
            Command->taskFreeTail = gcvNULL;
        }

        /* No, just the head. */
        else {
            /* Update the head. */
            Command->taskFreeHead = Buffer->freeNext;

            /* Terminate the next buffer. */
            Command->taskFreeHead->freePrev = gcvFREE_TASK_TERMINATOR;
        }
    } else { /* Not the head. */
        /* Tail buffer? */
        if (Buffer->freeNext == gcvFREE_TASK_TERMINATOR) {
            /* Update the tail. */
            Command->taskFreeTail = Buffer->freePrev;

            /* Terminate the previous buffer. */
            Command->taskFreeTail->freeNext = gcvFREE_TASK_TERMINATOR;
        } else { /* A buffer in the middle. */
            /* Remove the buffer from the list. */
            Buffer->freePrev->freeNext = Buffer->freeNext;
            Buffer->freeNext->freePrev = Buffer->freePrev;
        }
    }

    /* Reset free list pointers. */
    Buffer->freePrev = gcvNULL;
    Buffer->freeNext = gcvNULL;
}

/*----------------------------------------------------------------------------*/
/*-------------------------- Task Buffer Allocation --------------------------*/

static void
_SplitTaskBuffer(IN gckVGCOMMAND Command, IN gcsTASK_CONTAINER_PTR Buffer, IN gctUINT Size)
{
    /* Determine the size of the new buffer. */
    gctINT splitBufferSize = Buffer->size - Size;

    gcmkASSERT(splitBufferSize >= 0);

    /* Is the split buffer big enough to become a separate buffer? */
    if (splitBufferSize >= gcvMIN_TASK_BUFFER) {
        /* Place the new path data. */
        gcsTASK_CONTAINER_PTR splitBuffer = (gcsTASK_CONTAINER_PTR)((gctUINT8_PTR)Buffer + Size);

        /* Set the trimmed buffer size. */
        Buffer->size = Size;

        /* Initialize the split buffer. */
        splitBuffer->referenceCount = 0;
        splitBuffer->size           = splitBufferSize;
        splitBuffer->freePrev       = gcvNULL;
        splitBuffer->freeNext       = gcvNULL;

        /* Link in. */
        _InsertTaskBuffer(Buffer, splitBuffer);
        _AppendToFreeList(Command, splitBuffer);
    }
}

static gceSTATUS
_AllocateTaskContainer(IN gckVGCOMMAND Command, IN gctUINT Size, OUT gcsTASK_CONTAINER_PTR *Buffer)
{
    gceSTATUS status;

    gcmkHEADER_ARG("Command=%p Size=0x%x, Buffer =%p", Command, Size, Buffer);

    /* Verify arguments. */
    gcmkVERIFY_ARGUMENT(Buffer != gcvNULL);

    do {
        gcsTASK_STORAGE_PTR   storage;
        gcsTASK_CONTAINER_PTR buffer;

        /* Adjust the size. */
        Size += gcmSIZEOF(gcsTASK_CONTAINER);

        /* Adjust the allocation size if not big enough. */
        if (Size > Command->taskStorageUsable) {
            Command->taskStorageGranularity = gcmALIGN(Size + gcmSIZEOF(gcsTASK_STORAGE), 1024);

            Command->taskStorageUsable =
                Command->taskStorageGranularity - gcmSIZEOF(gcsTASK_STORAGE);
        }

        /* Is there a free buffer available? */
        else if (Command->taskFreeHead != gcvNULL) {
            /* Set the initial free buffer. */
            gcsTASK_CONTAINER_PTR buffer = Command->taskFreeHead;

            do {
                /* Is the buffer big enough? */
                if (buffer->size >= Size) {
                    /* Remove the buffer from the free list. */
                    _RemoveFromFreeList(Command, buffer);

                    /* Split the buffer. */
                    _SplitTaskBuffer(Command, buffer, Size);

                    /* Set the result. */
                    *Buffer = buffer;

                    gcmkFOOTER_ARG("*Buffer=0x%x", *Buffer);
                    /* Success. */
                    return gcvSTATUS_OK;
                }

                /* Get the next free buffer. */
                buffer = buffer->freeNext;
            } while (buffer != gcvFREE_TASK_TERMINATOR);
        }

        /* Allocate a container. */
        gcmkERR_BREAK(gckOS_Allocate(Command->os,
                                     Command->taskStorageGranularity,
                                     (gctPOINTER *)&storage));

        /* Link in the storage buffer. */
        storage->next        = Command->taskStorage;
        Command->taskStorage = storage;

        /* Place the task buffer. */
        buffer = (gcsTASK_CONTAINER_PTR)(storage + 1);

        /* Determine the size of the buffer. */
        buffer->size = Command->taskStorageGranularity - gcmSIZEOF(gcsTASK_STORAGE);

        /* Initialize the task buffer. */
        buffer->referenceCount = 0;
        buffer->allocPrev      = gcvNULL;
        buffer->allocNext      = gcvNULL;
        buffer->freePrev       = gcvNULL;
        buffer->freeNext       = gcvNULL;

        /* Split the buffer. */
        _SplitTaskBuffer(Command, buffer, Size);

        /* Set the result. */
        *Buffer = buffer;

        gcmkFOOTER_ARG("*Buffer=%p", *Buffer);
        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    gcmkFOOTER();
    /* Return status. */
    return status;
}

static void
_FreeTaskContainer(IN gckVGCOMMAND Command, IN gcsTASK_CONTAINER_PTR Buffer)
{
    gcsTASK_CONTAINER_PTR prev;
    gcsTASK_CONTAINER_PTR next;
    gcsTASK_CONTAINER_PTR merged;

    gctUINT32 mergedSize;

    /* Verify arguments. */
    gcmkASSERT(Buffer != gcvNULL);
    gcmkASSERT(Buffer->freePrev == gcvNULL);
    gcmkASSERT(Buffer->freeNext == gcvNULL);

    /* Get shortcuts to the previous and next path data buffers. */
    prev = Buffer->allocPrev;
    next = Buffer->allocNext;

    /* Is the previous path data buffer already free? */
    if (prev && prev->freeNext) {
        /* The previous path data buffer is the one that remains. */
        merged = prev;

        /* Is the next path data buffer already free? */
        if (next && next->freeNext) {
            /* Merge all three path data buffers into the previous. */
            mergedSize = prev->size + Buffer->size + next->size;

            /* Remove the next path data buffer. */
            _RemoveFromFreeList(Command, next);
            _RemoveTaskBuffer(next);
        } else {
            /* Merge the current path data buffer into the previous. */
            mergedSize = prev->size + Buffer->size;
        }

        /* Delete the current path data buffer. */
        _RemoveTaskBuffer(Buffer);

        /* Set new size. */
        merged->size = mergedSize;
    } else {
        /* The current path data buffer is the one that remains. */
        merged = Buffer;

        /* Is the next buffer already free? */
        if (next && next->freeNext) {
            /* Merge the next into the current. */
            mergedSize = Buffer->size + next->size;

            /* Remove the next buffer. */
            _RemoveFromFreeList(Command, next);
            _RemoveTaskBuffer(next);

            /* Set new size. */
            merged->size = mergedSize;
        }

        /* Add the current buffer into the free list. */
        _AppendToFreeList(Command, merged);
    }
}

gceSTATUS
_RemoveRecordFromProcesDB(IN gckVGCOMMAND Command, IN gcsTASK_HEADER_PTR Task)
{
    gceSTATUS                       status;
    gcsTASK_PTR                     task = (gcsTASK_PTR)((gctUINT8_PTR)Task - sizeof(gcsTASK));
    gcsTASK_FREE_VIDEO_MEMORY_PTR   freeVideoMemory;
    gcsTASK_UNLOCK_VIDEO_MEMORY_PTR unlockVideoMemory;
    gctINT                          pid;
    gctUINT32                       size, id;
    gctUINT32                       handle;
    gctUINT64                       node;
    gckKERNEL                       kernel     = Command->kernel->kernel;
    gckVIDMEM_NODE                  unlockNode = gcvNULL;
    gckVIDMEM_NODE                  nodeObject = gcvNULL;
    gceDATABASE_TYPE                type;

    /* Get the total size of all tasks. */

    gcmkVERIFY_OK(gckOS_GetProcessID((gctUINT32_PTR)&pid));
    gcmkVERIFY_OK(gckOS_ReadMappedPointer(Command->os, &task->size, &size));

    do {
        gcmkVERIFY_OK(gckOS_ReadMappedPointer(Command->os, &Task->id, &id));
        switch (id) {
        case gcvTASK_FREE_VIDEO_MEMORY:
            freeVideoMemory = (gcsTASK_FREE_VIDEO_MEMORY_PTR)Task;

            gcmkVERIFY_OK(gckOS_ReadMappedPointer(Command->os, &freeVideoMemory->node, &handle));

            status = gckVIDMEM_HANDLE_Lookup(Command->kernel->kernel, pid, handle, &nodeObject);

            if (gcmIS_ERROR(status))
                return status;

            gckVIDMEM_HANDLE_Dereference(kernel, pid, handle);

            node = gcmPTR_TO_UINT64(nodeObject);

            gcmkVERIFY_OK(gckOS_CopyToUserData(Command->os, &node,
                                               &freeVideoMemory->node,
                                               gcmSIZEOF(gctUINT64)));

            type = gcvDB_VIDEO_MEMORY | (nodeObject->type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) |
                   (nodeObject->pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT);

            /* Remove record from process db. */
            gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(Command->kernel->kernel, pid,
                                                    type, gcmINT2PTR(handle)));

            /* Advance to next task. */
            size -= sizeof(gcsTASK_FREE_VIDEO_MEMORY);
            Task = (gcsTASK_HEADER_PTR)(freeVideoMemory + 1);

            break;
        case gcvTASK_UNLOCK_VIDEO_MEMORY:
            unlockVideoMemory = (gcsTASK_UNLOCK_VIDEO_MEMORY_PTR)Task;

            gcmkVERIFY_OK(gckOS_ReadMappedPointer(Command->os, &unlockVideoMemory->node, &handle));

            status = gckVIDMEM_HANDLE_Lookup(Command->kernel->kernel, pid, handle, &unlockNode);

            if (gcmIS_ERROR(status))
                return status;

            gckVIDMEM_HANDLE_Dereference(kernel, pid, handle);

            node = gcmPTR_TO_UINT64(unlockNode);

            gcmkVERIFY_OK(gckOS_CopyToUserData(Command->os, &node,
                                               &unlockVideoMemory->node,
                                               gcmSIZEOF(gctUINT64)));

            /* Remove record from process db. */
            gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB(Command->kernel->kernel, pid,
                                                    gcvDB_VIDEO_MEMORY_LOCKED,
                                                    gcmUINT64_TO_PTR(handle)));

            /* Advance to next task. */
            size -= sizeof(gcsTASK_UNLOCK_VIDEO_MEMORY);
            Task = (gcsTASK_HEADER_PTR)(unlockVideoMemory + 1);

            break;
        default:
            /* Skip the whole task. */
            size = 0;
            break;
        }
    } while (size);

    return gcvSTATUS_OK;
}

/******************************************************************************
 ********************************* Task Scheduling ****************************
 ******************************************************************************/

static gceSTATUS
_ScheduleTasks(IN gckVGCOMMAND Command, IN gcsTASK_MASTER_TABLE_PTR TaskTable,
               IN gctUINT8_PTR PreviousEnd)
{
    gceSTATUS status;

    do {
        gctINT                   block;
        gcsTASK_CONTAINER_PTR    container;
        gcsTASK_MASTER_ENTRY_PTR userTaskEntry;
        gcsBLOCK_TASK_ENTRY_PTR  kernelTaskEntry;
        gcsTASK_PTR              userTask;
        gcsTASK                  userTaskObject;
        gctUINT8_PTR             kernelTask;
        gctINT32                 interrupt;
        gctUINT8_PTR             eventCommand;
        gctBOOL                  needCopy = gcvFALSE;
        gctINT                   pid;
#ifdef __QNXNTO__
        gcsTASK_PTR oldUserTask = gcvNULL;
        gctPOINTER  pointer;
#    endif

        /* Nothing to schedule? */
        if (TaskTable->size == 0) {
            status = gcvSTATUS_OK;
            break;
        }

        /* Acquire the mutex. */
        gcmkERR_BREAK(gckOS_AcquireMutex(Command->os, Command->taskMutex, gcvINFINITE));

        gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                       "%s(%d)\n", __FUNCTION__, __LINE__);

        gcmkVERIFY_OK(gckOS_GetProcessID((gctUINT32_PTR)&pid));
        gcmkVERIFY_OK(gckOS_QueryNeedCopy(Command->os, pid, &needCopy));
        do {
            gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                           "  number of tasks scheduled   = %d\n"
                           "  size of event data in bytes = %d\n",
                           TaskTable->count, TaskTable->size);

            /* Allocate task buffer. */
            gcmkERR_BREAK(_AllocateTaskContainer(Command, TaskTable->size, &container));

            /* Determine the task data pointer. */
            kernelTask = (gctUINT8_PTR)(container + 1);

            /* Initialize the reference count. */
            container->referenceCount = TaskTable->count;

            /* Process tasks. */
            for (block = gcvBLOCK_COUNT - 1; block >= 0; block -= 1) {
                /* Get the current user table entry. */
                userTaskEntry = &TaskTable->table[block];

                /* Are there tasks scheduled? */
                if (userTaskEntry->head == gcvNULL) {
                    /* No, skip to the next block. */
                    continue;
                }

                gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                               "  processing tasks for block %d\n", block);

                /* Get the current kernel table entry. */
                kernelTaskEntry = &Command->taskTable[block];

                /* Are there tasks for the current block scheduled? */
                if (kernelTaskEntry->container == gcvNULL) {
                    gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                                   "  first task container for the block %d added\n", block);

                    /* Nothing yet, set the container buffer pointer. */
                    kernelTaskEntry->container = container;
                    kernelTaskEntry->task      = (gcsTASK_HEADER_PTR)kernelTask;
                } else { /* Yes, append to the end. */
                    kernelTaskEntry->link->cotainer = container;
                    kernelTaskEntry->link->task     = (gcsTASK_HEADER_PTR)kernelTask;
                }

                /* Set initial task. */
                userTask = userTaskEntry->head;

                gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                               "  copying user tasks over to the kernel\n");

                /* Copy tasks. */
                do {
                    gcsTASK_HEADER_PTR taskHeader;
#ifdef __QNXNTO__
                    oldUserTask = userTask;

                    gcmkERR_BREAK(gckOS_MapUserPointer(Command->os, oldUserTask, 0, &pointer));

                    userTask = pointer;
#    endif
                    taskHeader = (gcsTASK_HEADER_PTR)(userTask + 1);
                    if (needCopy) {
                        gcmkERR_BREAK(gckOS_CopyFromUserData(Command->os, &userTaskObject,
                                                             userTask, gcmSIZEOF(gcsTASK)));
                        userTask = &userTaskObject;
                    }

                    gcmkVERIFY_OK(_RemoveRecordFromProcesDB(Command, taskHeader));

                    gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                                   "    task ID = %d, size = %d\n",
                                   ((gcsTASK_HEADER_PTR)(userTask + 1))->id, userTask->size);

                    /* Copy the task data. */
                    if (needCopy) {
                        gcmkERR_BREAK(gckOS_CopyFromUserData(Command->os, kernelTask,
                                                             taskHeader, userTask->size));
                    } else {
                        gcmkERR_BREAK(gckOS_MemCopy(kernelTask, taskHeader, userTask->size));
                    }
#ifdef __QNXNTO__
                    if (taskHeader->id == gcvTASK_SIGNAL) {
                        gcsTASK_SIGNAL_PTR taskSignal = (gcsTASK_SIGNAL_PTR)kernelTask;
                        gctPOINTER         signal;
                        gctUINT32          pid;

                        gcmkVERIFY_OK(gckOS_GetProcessID(&pid));

                        taskSignal->rcvid = TaskTable->rcvid;
                        taskSignal->event = TaskTable->event;

                        gcmkERR_BREAK(drv_signal_mgr_add(pid, taskSignal->rcvid,
                                                         &taskSignal->event,
                                                         gcmPTR_TO_UINT64(taskSignal->signal),
                                                         &signal));

                        taskSignal->signal = signal;
                    }
#    endif

                    /* Advance to the next task. */
                    kernelTask += userTask->size;
                    userTask = userTask->next;

#ifdef __QNXNTO__
                    gcmkERR_BREAK(gckOS_UnmapUserPointer(Command->os, oldUserTask, 0, pointer));
#    endif
                } while (userTask != gcvNULL);

                /* Update link pointer in the header. */
                kernelTaskEntry->link = (gcsTASK_LINK_PTR)kernelTask;

                /* Initialize link task. */
                kernelTaskEntry->link->id       = gcvTASK_LINK;
                kernelTaskEntry->link->cotainer = gcvNULL;
                kernelTaskEntry->link->task     = gcvNULL;

                /* Advance the task data pointer. */
                kernelTask += gcmSIZEOF(gcsTASK_LINK);
            }
        } while (gcvFALSE);

        /* Release the mutex. */
        gcmkERR_BREAK(gckOS_ReleaseMutex(Command->os, Command->taskMutex));

        /* Assign interrupts to the blocks. */
        eventCommand = PreviousEnd;

        for (block = gcvBLOCK_COUNT - 1; block >= 0; block -= 1) {
            /* Get the current user table entry. */
            userTaskEntry = &TaskTable->table[block];

            /* Are there tasks scheduled? */
            if (userTaskEntry->head == gcvNULL) {
                /* No, skip to the next block. */
                continue;
            }

            /* Get the interrupt number. */
            interrupt = _GetNextInterrupt(Command, block);

            gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                           "%s(%d): block = %d interrupt = %d\n",
                           __FUNCTION__, __LINE__, block, interrupt);

            /* Determine the command position. */
            eventCommand -= Command->info.eventCommandSize;

            /* Append an EVENT command. */
            gcmkERR_BREAK(gckVGCOMMAND_EventCommand(Command, eventCommand,
                                                    block, interrupt, gcvNULL));
        }
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

/******************************************************************************
 ******************************** Memory Management ***************************
 ******************************************************************************/

static gceSTATUS
_HardwareToKernel(IN gckOS Os,
                  IN gcuVIDMEM_NODE_PTR Node,
                  IN gctUINT32 Address,
                  OUT gctPOINTER *KernelPointer)
{
    gceSTATUS   status;
    gckVIDMEM   memory;
    gctUINT32   offset;
    gctUINT32   nodePhysical;
    gctPOINTER *logical;
    gctSIZE_T   bytes;

    status = gcvSTATUS_OK;

    memory = Node->VidMem.parent;

    if (memory->object.type == gcvOBJ_VIDMEM) {
        nodePhysical = (gctUINT32)memory->physicalBase +
                       (gctUINT32)Node->VidMem.offset +
                       Node->VidMem.alignment;

        bytes   = Node->VidMem.bytes;
        logical = &Node->VidMem.kernelVirtual;
    } else {
        gcmkSAFECASTPHYSADDRT(nodePhysical, Node->Virtual.physicalAddress);
        bytes   = Node->Virtual.bytes;
        logical = &Node->Virtual.kernelVirtual;
    }

    if (*logical == gcvNULL) {
        status = gckOS_MapPhysical(Os, nodePhysical, bytes, logical);

        if (gcmkIS_ERROR(status))
            return status;
    }

    offset         = Address - nodePhysical;
    *KernelPointer = (gctPOINTER)((gctUINT8_PTR)(*logical) + offset);

    /* Return status. */
    return status;
}

static gceSTATUS
_ConvertUserCommandBufferPointer(IN gckVGCOMMAND Command,
                                 IN gcsCMDBUFFER_PTR UserCommandBuffer,
                                 OUT gcsCMDBUFFER_PTR *KernelCommandBuffer)
{
    gceSTATUS        status, last;
    gcsCMDBUFFER_PTR mappedUserCommandBuffer = gcvNULL;
    gcsCMDBUFFER     _CommandBufferObject;
    gckKERNEL        kernel = Command->kernel->kernel;
    gctUINT32        pid;
    gckVIDMEM_NODE   node;
    gctBOOL          needCopy = gcvFALSE;

    gckOS_GetProcessID(&pid);

    do {
        gctUINT32 headerAddress;

        gcmkERR_BREAK(gckOS_QueryNeedCopy(Command->os, pid, &needCopy));
        if (needCopy) {
            gcmkERR_BREAK(gckOS_CopyFromUserData(Command->os, &_CommandBufferObject,
                                                 UserCommandBuffer, gcmSIZEOF(gcsCMDBUFFER)));
            mappedUserCommandBuffer = &_CommandBufferObject;
        } else {
            /* Map the command buffer structure into the kernel space. */
            gcmkERR_BREAK(gckOS_MapUserPointer(Command->os, UserCommandBuffer,
                                               gcmSIZEOF(gcsCMDBUFFER),
                                               (gctPOINTER *)&mappedUserCommandBuffer));
        }
        /* Determine the address of the header. */
        headerAddress = mappedUserCommandBuffer->address - mappedUserCommandBuffer->bufferOffset;

        gcmkERR_BREAK(gckVIDMEM_HANDLE_Lookup(kernel, pid,
                                              gcmPTR2INT32(mappedUserCommandBuffer->node),
                                              &node));

        /* Translate the logical address to the kernel space. */
        gcmkERR_BREAK(_HardwareToKernel(Command->os, node->node, headerAddress,
                                        (gctPOINTER *)KernelCommandBuffer));
    } while (gcvFALSE);

    /* Unmap the user command buffer. */
    if (mappedUserCommandBuffer != gcvNULL && needCopy == gcvFALSE) {
        gcmkCHECK_STATUS(gckOS_UnmapUserPointer(Command->os, UserCommandBuffer,
                                                gcmSIZEOF(gcsCMDBUFFER),
                                                mappedUserCommandBuffer));
    }

    /* Return status. */
    return status;
}

static gceSTATUS
_AllocateLinear(IN gckVGCOMMAND Command,
                IN gctUINT Size,
                IN gctUINT Alignment,
                OUT gcuVIDMEM_NODE_PTR *Node,
                OUT gctUINT32 *Address,
                OUT gctPOINTER *Logical)
{
    gceSTATUS      status, last;
    gctPOINTER     logical;
    gctPHYS_ADDR   physical;
    gctUINT32      address;
    gctSIZE_T      size = Size;
    gctPHYS_ADDR_T paddr;

    do {
        gcmkERR_BREAK(gckOS_AllocateNonPagedMemory(Command->os,
                                                   Command->kernel->kernel,
                                                   gcvFALSE,
                                                   gcvALLOC_FLAG_CONTIGUOUS | gcvALLOC_FLAG_4GB_ADDR,
                                                   &size,
                                                   &physical,
                                                   &logical));

        gcmkERR_BREAK(gckOS_GetPhysicalFromHandle(Command->os, physical, 0, &paddr));

        gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Command->os, paddr, &paddr));

        gcmkSAFECASTPHYSADDRT(address, paddr);

        /* Set return values. */
        *Node    = physical;
        *Address = address;
        *Logical = logical;

        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    /* Roll back. */
    if (physical != gcvNULL) {
        /* Free the command buffer. */
        gcmkCHECK_STATUS(gckOS_FreeNonPagedMemory(Command->os, physical, logical, size));
    }

    /* Return status. */
    return status;
}

static gceSTATUS
_FreeLinear(IN gckVGKERNEL Kernel, IN gcuVIDMEM_NODE_PTR Node, IN gctPOINTER Logical)
{
    gceSTATUS status = gcvSTATUS_OK;

    do {
        gcmkERR_BREAK(gckOS_FreeNonPagedMemory(Kernel->os, Node, Logical, 1));
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

gceSTATUS
_AllocateCommandBuffer(IN gckVGCOMMAND Command, IN gctSIZE_T Size,
                       OUT gcsCMDBUFFER_PTR *CommandBuffer)
{
    gceSTATUS          status, last;
    gcuVIDMEM_NODE_PTR node          = gcvNULL;
    gcsCMDBUFFER_PTR   commandBuffer = gcvNULL;

    do {
        gctUINT      alignedHeaderSize;
        gctUINT      requestedSize;
        gctUINT      allocationSize;
        gctUINT32    address = 0;
        gctUINT8_PTR endCommand;

        /* Determine the aligned header size. */
        alignedHeaderSize =
            (gctUINT32)gcmALIGN(gcmSIZEOF(gcsCMDBUFFER), Command->info.addressAlignment);

        /* Align the requested size. */
        requestedSize = (gctUINT32)gcmALIGN(Size, Command->info.commandAlignment);

        /* Determine the size of the buffer to allocate. */
        allocationSize =
            alignedHeaderSize + requestedSize + (gctUINT32)Command->info.staticTailSize;

        /* Allocate the command buffer. */
        gcmkERR_BREAK(_AllocateLinear(Command, allocationSize,
                                      Command->info.addressAlignment,
                                      &node, &address, (gctPOINTER *)&commandBuffer));

        /* Initialize the structure. */
        commandBuffer->completion    = gcvVACANT_BUFFER;
        commandBuffer->node          = node;
        commandBuffer->address       = address + alignedHeaderSize;
        commandBuffer->bufferOffset  = alignedHeaderSize;
        commandBuffer->size          = requestedSize;
        commandBuffer->offset        = requestedSize;
        commandBuffer->nextAllocated = gcvNULL;
        commandBuffer->nextSubBuffer = gcvNULL;

        /* Determine the data count. */
        commandBuffer->dataCount =
            (requestedSize + Command->info.staticTailSize) / Command->info.commandAlignment;

        /* Determine the location of the END command. */
        endCommand = (gctUINT8_PTR)commandBuffer + alignedHeaderSize + requestedSize;

        /* Append an END command. */
        gcmkERR_BREAK(gckVGCOMMAND_EndCommand(Command, endCommand,
                                              Command->info.feBufferInt, gcvNULL));

        /* Set the return pointer. */
        *CommandBuffer = commandBuffer;

        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    /* Roll back. */
    if (node != gcvNULL) {
        /* Free the command buffer. */
        gcmkCHECK_STATUS(_FreeLinear(Command->kernel, node, commandBuffer));
    }

    /* Return status. */
    return status;
}

static gceSTATUS
_FreeCommandBuffer(IN gckVGKERNEL Kernel, IN gcsCMDBUFFER_PTR CommandBuffer)
{
    gceSTATUS status;

    /* Free the buffer. */
    status = _FreeLinear(Kernel, CommandBuffer->node, CommandBuffer);

    /* Return status. */
    return status;
}

/******************************************************************************
 ****************************** TS Overflow Handler ***************************
 ******************************************************************************/

static gceSTATUS
_EventHandler_TSOverflow(IN gckVGKERNEL Kernel)
{
    gcmkTRACE(gcvLEVEL_ERROR, "%s(%d): **** TS OVERFLOW ENCOUNTERED ****\n",
              __FUNCTION__, __LINE__);

    return gcvSTATUS_OK;
}

/******************************************************************************
 ****************************** Bus Error Handler *****************************
 ******************************************************************************/

static gceSTATUS
_EventHandler_BusError(IN gckVGKERNEL Kernel)
{
    gcmkTRACE(gcvLEVEL_ERROR, "%s(%d): **** BUS ERROR ENCOUNTERED ****\n",
              __FUNCTION__, __LINE__);

    return gcvSTATUS_OK;
}

/******************************************************************************
 ****************************** Power Stall Handler ***************************
 ******************************************************************************/

static gceSTATUS
_EventHandler_PowerStall(IN gckVGKERNEL Kernel)
{
    /* Signal. */
    return gckOS_Signal(Kernel->os, Kernel->command->powerStallSignal, gcvTRUE);
}

/******************************************************************************
 ******************************** Task Routines *******************************
 ******************************************************************************/

typedef gceSTATUS (*gctTASKROUTINE) (gckVGCOMMAND Command,
                                     gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskLink(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskCluster(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskIncrement(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskDecrement(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskSignal(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskLockdown(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskUnlockVideoMemory(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskFreeVideoMemory(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gceSTATUS
_TaskFreeContiguousMemory(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader);

static gctTASKROUTINE _taskRoutine[] = {
    _TaskLink,                 /* gcvTASK_LINK                   */
    _TaskCluster,              /* gcvTASK_CLUSTER                */
    _TaskIncrement,            /* gcvTASK_INCREMENT              */
    _TaskDecrement,            /* gcvTASK_DECREMENT              */
    _TaskSignal,               /* gcvTASK_SIGNAL                 */
    _TaskLockdown,             /* gcvTASK_LOCKDOWN               */
    _TaskUnlockVideoMemory,    /* gcvTASK_UNLOCK_VIDEO_MEMORY    */
    _TaskFreeVideoMemory,      /* gcvTASK_FREE_VIDEO_MEMORY      */
    _TaskFreeContiguousMemory, /* gcvTASK_FREE_CONTIGUOUS_MEMORY */
};

static gceSTATUS
_TaskLink(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    /* Cast the task pointer. */
    gcsTASK_LINK_PTR task = (gcsTASK_LINK_PTR)TaskHeader->task;

    /* Save the pointer to the container. */
    gcsTASK_CONTAINER_PTR container = TaskHeader->container;

    /* No more tasks in the list? */
    if (task->task == gcvNULL) {
        /* Reset the entry. */
        TaskHeader->container = gcvNULL;
        TaskHeader->task      = gcvNULL;
        TaskHeader->link      = gcvNULL;
    } else {
        /* Update the entry. */
        TaskHeader->container = task->cotainer;
        TaskHeader->task      = task->task;
    }

    /* Decrement the task buffer reference. */
    gcmkASSERT(container->referenceCount >= 0);
    if (container->referenceCount == 0) {
        /* Free the container. */
        _FreeTaskContainer(Command, container);
    }

    /* Success. */
    return gcvSTATUS_OK;
}

static gceSTATUS
_TaskCluster(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status = gcvSTATUS_OK;

    /* Cast the task pointer. */
    gcsTASK_CLUSTER_PTR cluster = (gcsTASK_CLUSTER_PTR)TaskHeader->task;

    /* Get the number of tasks. */
    gctUINT taskCount = cluster->taskCount;

    /* Advance to the next task. */
    TaskHeader->task = (gcsTASK_HEADER_PTR)(cluster + 1);

    /* Perform all tasks in the cluster. */
    while (taskCount) {
        /* Perform the current task. */
        gcmkERR_BREAK(_taskRoutine[TaskHeader->task->id](Command, TaskHeader));

        /* Update the task count. */
        taskCount -= 1;
    }

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskIncrement(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status;

    do {
        /* Cast the task pointer. */
        gcsTASK_INCREMENT_PTR task = (gcsTASK_INCREMENT_PTR)TaskHeader->task;

        /* Convert physical into logical address. */
        gctUINT32_PTR logical = gcvNULL;

        gcmkERR_BREAK(gckOS_MapPhysical(Command->os, task->address,
                                        gcmSIZEOF(gctUINT32),
                                        (gctPOINTER *)&logical));

        /* Increment data. */
        (*logical) += 1;

        /* Unmap the physical memory. */
        gcmkERR_BREAK(gckOS_UnmapPhysical(Command->os, logical, gcmSIZEOF(gctUINT32)));

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskDecrement(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status;

    do {
        /* Cast the task pointer. */
        gcsTASK_DECREMENT_PTR task = (gcsTASK_DECREMENT_PTR)TaskHeader->task;

        /* Convert physical into logical address. */
        gctUINT32_PTR logical = gcvNULL;

        gcmkERR_BREAK(gckOS_MapPhysical(Command->os, task->address,
                                        gcmSIZEOF(gctUINT32),
                                        (gctPOINTER *)&logical));

        /* Decrement data. */
        (*logical) -= 1;

        /* Unmap the physical memory. */
        gcmkERR_BREAK(gckOS_UnmapPhysical(Command->os, logical, gcmSIZEOF(gctUINT32)));

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskSignal(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status;

    do {
        /* Cast the task pointer. */
        gcsTASK_SIGNAL_PTR task = (gcsTASK_SIGNAL_PTR)TaskHeader->task;

        /* Map the signal into kernel space. */
#ifdef __QNXNTO__
        status = gckOS_UserSignal(Command->os, task->signal, task->rcvid, &task->event);
#    else
        status = gckOS_UserSignal(Command->os, task->signal, task->process);
#    endif /* __QNXNTO__ */

        if (gcmIS_ERROR(status)) {
            if (status == gcvSTATUS_NOT_FOUND)
                status = gcvSTATUS_OK;
            else
                break;
        }

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskLockdown(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS     status;
    gctUINT32_PTR userCounter   = gcvNULL;
    gctUINT32_PTR kernelCounter = gcvNULL;
    gctSIGNAL     signal        = gcvNULL;

    do {
        /* Cast the task pointer. */
        gcsTASK_LOCKDOWN_PTR task = (gcsTASK_LOCKDOWN_PTR)TaskHeader->task;

        /* Convert physical addresses into logical. */
        gcmkERR_BREAK(gckOS_MapPhysical(Command->os, task->userCounter,
                                        gcmSIZEOF(gctUINT32),
                                        (gctPOINTER *)&userCounter));

        gcmkERR_BREAK(gckOS_MapPhysical(Command->os, task->kernelCounter,
                                        gcmSIZEOF(gctUINT32),
                                        (gctPOINTER *)&kernelCounter));

        /* Update the kernel counter. */
        (*kernelCounter) += 1;

        /* Are the counters equal? */
        if ((*userCounter) == (*kernelCounter)) {
            /* Map the signal into kernel space. */
            gcmkERR_BREAK(gckOS_MapSignal(Command->os, task->signal, task->process, &signal));

            if (signal == gcvNULL) {
                /* Signal. */
                gcmkERR_BREAK(gckOS_Signal(Command->os, task->signal, gcvTRUE));
            } else {
                /* Signal. */
                gcmkERR_BREAK(gckOS_Signal(Command->os, signal, gcvTRUE));
            }
        }

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Destroy the mapped signal. */
    if (signal != gcvNULL)
        gcmkVERIFY_OK(gckOS_DestroySignal(Command->os, signal));

    /* Unmap the physical memory. */
    if (kernelCounter != gcvNULL)
        gcmkVERIFY_OK(gckOS_UnmapPhysical(Command->os, kernelCounter, gcmSIZEOF(gctUINT32)));

    if (userCounter != gcvNULL)
        gcmkVERIFY_OK(gckOS_UnmapPhysical(Command->os, userCounter, gcmSIZEOF(gctUINT32)));

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskUnlockVideoMemory(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status;

    do {
        gctUINT32 processID;

        /* Cast the task pointer. */
        gcsTASK_UNLOCK_VIDEO_MEMORY_PTR task = (gcsTASK_UNLOCK_VIDEO_MEMORY_PTR)TaskHeader->task;

        gckOS_GetProcessID(&processID);

        /* Unlock video memory. */
        gcmkERR_BREAK(gckVIDMEM_NODE_Unlock(Command->kernel->kernel,
                                            (gckVIDMEM_NODE)gcmUINT64_TO_PTR(task->node),
                                            processID, gcvNULL));

        gcmkERR_BREAK(gckVIDMEM_NODE_Dereference(Command->kernel->kernel,
                                                 gcmUINT64_TO_PTR(task->node)));

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskFreeVideoMemory(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status;

    do {
        /* Cast the task pointer. */
        gcsTASK_FREE_VIDEO_MEMORY_PTR task = (gcsTASK_FREE_VIDEO_MEMORY_PTR)TaskHeader->task;

        /* Free video memory. */
        gcmkERR_BREAK(gckVIDMEM_NODE_Dereference(Command->kernel->kernel,
                                                 gcmUINT64_TO_PTR(task->node)));

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_TaskFreeContiguousMemory(gckVGCOMMAND Command, gcsBLOCK_TASK_ENTRY_PTR TaskHeader)
{
    gceSTATUS status;

    do {
        /* Cast the task pointer. */
        gcsTASK_FREE_CONTIGUOUS_MEMORY_PTR task =
            (gcsTASK_FREE_CONTIGUOUS_MEMORY_PTR)TaskHeader->task;

        /* Free contiguous memory. */
        gcmkERR_BREAK(gckOS_FreeNonPagedMemory(Command->os, task->physical,
                                               task->logical, task->bytes));

        /* Update the reference counter. */
        TaskHeader->container->referenceCount -= 1;

        /* Update the task pointer. */
        TaskHeader->task = (gcsTASK_HEADER_PTR)(task + 1);
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

/******************************************************************************
 ************ Hardware Block Interrupt Handlers For Scheduled Events **********
 ******************************************************************************/

static gceSTATUS
_EventHandler_Block(IN gckVGKERNEL Kernel,
                    IN gcsBLOCK_TASK_ENTRY_PTR TaskHeader,
                    IN gctBOOL ProcessAll)
{
    gceSTATUS status = gcvSTATUS_OK, last;

    gcmkHEADER_ARG("Kernel=%p TaskHeader=%p ProcessAll=0x%x",
                   Kernel, TaskHeader, ProcessAll);
    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);

    if (TaskHeader->task == gcvNULL) {
        gcmkFOOTER();
        return gcvSTATUS_OK;
    }

    do {
        gckVGCOMMAND command;

        /* Get the command buffer object. */
        command = Kernel->command;

        /* Increment the interrupt usage semaphore. */
        gcmkERR_BREAK(gckOS_IncrementSemaphore(command->os, TaskHeader->interruptSemaphore));

        /* Acquire the mutex. */
        gcmkERR_BREAK(gckOS_AcquireMutex(command->os, command->taskMutex, gcvINFINITE));

        /* Verify inputs. */
        gcmkASSERT(TaskHeader != gcvNULL);
        gcmkASSERT(TaskHeader->container != gcvNULL);
        gcmkASSERT(TaskHeader->task != gcvNULL);
        gcmkASSERT(TaskHeader->link != gcvNULL);

        /* Process tasks. */
        do {
            /* Process the current task. */
            gcmkERR_BREAK(_taskRoutine[TaskHeader->task->id](command, TaskHeader));

            /* Is the next task is LINK? */
            if (TaskHeader->task->id == gcvTASK_LINK) {
                gcmkERR_BREAK(_taskRoutine[TaskHeader->task->id](command, TaskHeader));

                /* Done. */
                break;
            }
        } while (ProcessAll);

        /* Release the mutex. */
        gcmkCHECK_STATUS(gckOS_ReleaseMutex(command->os, command->taskMutex));
    } while (gcvFALSE);

    gcmkFOOTER();
    /* Return status. */
    return status;
}

gcmDECLARE_INTERRUPT_HANDLER(COMMAND, 0)
{
    gceSTATUS status, last;

    gcmkHEADER_ARG("Kernel=%p ", Kernel);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);

    do {
        gckVGCOMMAND               command;
        gcsKERNEL_QUEUE_HEADER_PTR mergeQueue;
        gcsKERNEL_QUEUE_HEADER_PTR queueTail;
        gcsKERNEL_CMDQUEUE_PTR     entry;
        gctUINT                    entryCount;

        /* Get the command buffer object. */
        command = Kernel->command;

        /* Acquire the mutex. */
        gcmkERR_BREAK(gckOS_AcquireMutex(command->os, command->queueMutex, gcvINFINITE));

        /* Get the current queue. */
        queueTail = command->queueTail;

        /* Get the current queue entry. */
        entry = queueTail->currentEntry;

        /* Get the number of entries in the queue. */
        entryCount = queueTail->pending;

        /* Process all entries. */
        while (entryCount > 0) {
            /* Call post-execution function. */
            status = entry->handler(Kernel, entry);

            /* Failed? */
            if (gcmkIS_ERROR(status)) {
                gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_COMMAND,
                               "[%s] line %d: post action failed.\n",
                               __FUNCTION__, __LINE__);
            }

            /* Executed the next buffer? */
            if (status == gcvSTATUS_EXECUTED) {
                /* Update the queue. */
                queueTail->pending      = entryCount;
                queueTail->currentEntry = entry;

                /* Success. */
                status = gcvSTATUS_OK;

                /* Break out of the loop. */
                break;
            }

            /* Advance to the next entry. */
            entry += 1;
            entryCount -= 1;

            /* Last entry? */
            if (entryCount == 0) {
                /* Reset the queue to idle. */
                queueTail->pending = 0;

                /* Get a shortcut to the queue to merge with. */
                mergeQueue = command->mergeQueue;

                /* Merge the queues if necessary. */
                if (mergeQueue != queueTail) {
                    gcmkASSERT(mergeQueue < queueTail);
                    gcmkASSERT(mergeQueue->next == queueTail);

                    mergeQueue->size += gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) + queueTail->size;

                    mergeQueue->next = queueTail->next;
                }

                /* Advance to the next queue. */
                queueTail = queueTail->next;

                /* Did it wrap around? */
                if (command->queue == queueTail) {
                    /* Reset merge queue. */
                    command->mergeQueue = queueTail;
                }

                /* Set new queue. */
                command->queueTail = queueTail;

                /* Is the next queue scheduled? */
                if (queueTail->pending > 0) {
                    gcsCMDBUFFER_PTR commandBuffer;

                    /* The first entry must be a command buffer. */
                    commandBuffer = queueTail->currentEntry->commandBuffer;

                    /* Start the command processor. */
                    status = gckVGHARDWARE_Execute(command->hardware,
                                                   commandBuffer->address,
                                                   commandBuffer->dataCount);

                    /* Failed? */
                    if (gcmkIS_ERROR(status)) {
                        gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_COMMAND,
                                       "[%s] line %d: failed to start the next queue.\n",
                                       __FUNCTION__, __LINE__);
                    }
                } else {
                    status = gckVGHARDWARE_SetPowerState(Kernel->command->hardware,
                                                         gcvPOWER_IDLE_BROADCAST);
                }

                /* Break out of the loop. */
                break;
            }
        }

        /* Release the mutex. */
        gcmkCHECK_STATUS(gckOS_ReleaseMutex(command->os, command->queueMutex));
    } while (gcvFALSE);

    gcmkFOOTER();
    /* Return status. */
    return status;
}

/* Define standard block interrupt handlers. */
gcmDEFINE_INTERRUPT_HANDLER(TESSELLATOR, 0)
gcmDEFINE_INTERRUPT_HANDLER(VG,          0)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       0)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       1)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       2)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       3)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       4)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       5)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       6)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       7)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       8)
gcmDEFINE_INTERRUPT_HANDLER(PIXEL,       9)

    /* The entries in the array are arranged by event priority. */
static gcsBLOCK_INTERRUPT_HANDLER _blockHandlers[] = {
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(TESSELLATOR, 0),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(VG,          0),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       0),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       1),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       2),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       3),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       4),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       5),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       6),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       7),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       8),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL,       9),
    gcmDEFINE_INTERRUPT_HANDLER_ENTRY(COMMAND,     0),
};

/******************************************************************************
 ************************* Static Command Buffer Handlers *********************
 ******************************************************************************/

static gceSTATUS
_UpdateStaticCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                   "%s(%d)\n", __FUNCTION__, __LINE__);

    /* Success. */
    return gcvSTATUS_OK;
}

static gceSTATUS
_ExecuteStaticCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gceSTATUS status;

    do {
        gcsCMDBUFFER_PTR commandBuffer;

        /* Cast the command buffer header. */
        commandBuffer = Entry->commandBuffer;

        /* Set to update the command buffer next time. */
        Entry->handler = _UpdateStaticCommandBuffer;

        gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                       "%s(%d): executing next buffer @ 0x%08X, data count = %d\n",
                       __FUNCTION__, __LINE__,
                       commandBuffer->address, commandBuffer->dataCount);

        /* Start the command processor. */
        gcmkERR_BREAK(gckVGHARDWARE_Execute(Kernel->hardware,
                                            commandBuffer->address,
                                            commandBuffer->dataCount));

        /* Success. */
        return gcvSTATUS_EXECUTED;
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_UpdateLastStaticCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
#if gcvDEBUG || gcdFORCE_MESSAGES
    /* Get the command buffer header. */
    gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer;

    /* Validate the command buffer. */
    gcmkASSERT(commandBuffer->completion != gcvNULL);
    gcmkASSERT(commandBuffer->completion != gcvVACANT_BUFFER);

#    endif

    gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                   "%s(%d): processing all tasks scheduled for FE.\n",
                   __FUNCTION__, __LINE__);

    /* Perform scheduled tasks. */
    return _EventHandler_Block(Kernel,
                               &Kernel->command->taskTable[gcvBLOCK_COMMAND],
                               gcvTRUE);
}

static gceSTATUS
_ExecuteLastStaticCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gceSTATUS status;

    do {
        /* Cast the command buffer header. */
        gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer;

        /* Set to update the command buffer next time. */
        Entry->handler = _UpdateLastStaticCommandBuffer;

        gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                       "%s(%d): executing next buffer @ 0x%08X, data count = %d\n",
                       __FUNCTION__, __LINE__,
                       commandBuffer->address, commandBuffer->dataCount);

        /* Start the command processor. */
        gcmkERR_BREAK(gckVGHARDWARE_Execute(Kernel->hardware,
                                            commandBuffer->address,
                                            commandBuffer->dataCount));

        /* Success. */
        return gcvSTATUS_EXECUTED;
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

/******************************************************************************
 ************************* Dynamic Command Buffer Handlers ********************
 ******************************************************************************/

static gceSTATUS
_UpdateDynamicCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                   "%s(%d)\n", __FUNCTION__, __LINE__);

    /* Success. */
    return gcvSTATUS_OK;
}

static gceSTATUS
_ExecuteDynamicCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gceSTATUS status;

    do {
        /* Cast the command buffer header. */
        gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer;

        /* Set to update the command buffer next time. */
        Entry->handler = _UpdateDynamicCommandBuffer;

        gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                       "%s(%d): executing next buffer @ 0x%08X, data count = %d\n",
                       __FUNCTION__, __LINE__,
                       commandBuffer->address, commandBuffer->dataCount);

        /* Start the command processor. */
        gcmkERR_BREAK(gckVGHARDWARE_Execute(Kernel->hardware,
                                            commandBuffer->address,
                                            commandBuffer->dataCount));

        /* Success. */
        return gcvSTATUS_EXECUTED;
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_UpdateLastDynamicCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
#if gcvDEBUG || gcdFORCE_MESSAGES
    /* Get the command buffer header. */
    gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer;

    /* Validate the command buffer. */
    gcmkASSERT(commandBuffer->completion != gcvNULL);
    gcmkASSERT(commandBuffer->completion != gcvVACANT_BUFFER);

#    endif

    gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                   "%s(%d): processing all tasks scheduled for FE.\n",
                   __FUNCTION__, __LINE__);

    /* Perform scheduled tasks. */
    return _EventHandler_Block(Kernel,
                               &Kernel->command->taskTable[gcvBLOCK_COMMAND],
                               gcvTRUE);
}

static gceSTATUS
_ExecuteLastDynamicCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gceSTATUS status;

    do {
        /* Cast the command buffer header. */
        gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer;

        /* Set to update the command buffer next time. */
        Entry->handler = _UpdateLastDynamicCommandBuffer;

        gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
                       "%s(%d): executing next buffer @ 0x%08X, data count = %d\n",
                       __FUNCTION__, __LINE__,
                       commandBuffer->address, commandBuffer->dataCount);

        /* Start the command processor. */
        gcmkERR_BREAK(gckVGHARDWARE_Execute(Kernel->hardware,
                                            commandBuffer->address,
                                            commandBuffer->dataCount));

        /* Success. */
        return gcvSTATUS_EXECUTED;
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

/******************************************************************************
 ********************************* Other Handlers *****************************
 ******************************************************************************/

static gceSTATUS
_FreeKernelCommandBuffer(IN gckVGKERNEL Kernel, IN gcsKERNEL_CMDQUEUE_PTR Entry)
{
    gceSTATUS status;

    /* Free the command buffer. */
    status = _FreeCommandBuffer(Kernel, Entry->commandBuffer);

    /* Return status. */
    return status;
}

/******************************************************************************
 ******************************* Queue Management *****************************
 ******************************************************************************/

#if gcvDUMP_COMMAND_BUFFER
static void
_DumpCommandQueue(IN gckVGCOMMAND Command,
                  IN gcsKERNEL_QUEUE_HEADER_PTR QueueHeader,
                  IN gctUINT EntryCount)
{
    gcsKERNEL_CMDQUEUE_PTR entry;
    gctUINT                queueIndex;

#if defined(gcvCOMMAND_BUFFER_NAME)
    static gctUINT         arrayCount;
#        endif

    /* Is dumpinng enabled? */
    if (!Command->enableDumping)
        return;

#if !defined(gcvCOMMAND_BUFFER_NAME)
    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND,
                   "COMMAND QUEUE DUMP: %d entries\n", EntryCount);
#        endif

    /* Get the pointer to the first entry. */
    entry = QueueHeader->currentEntry;

    /* Iterate through the queue. */
    for (queueIndex = 0; queueIndex < EntryCount; queueIndex += 1) {
        gcsCMDBUFFER_PTR buffer;
        gctUINT          bufferCount;
        gctUINT          bufferIndex;
        gctUINT          i, count;
        gctUINT          size;
        gctUINT32_PTR    data;

#if gcvDUMP_COMMAND_LINES
        gctUINT lineNumber;
#        endif

#if !defined(gcvCOMMAND_BUFFER_NAME)
        gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "ENTRY %d\n", queueIndex);
#        endif

        /* Reset the count. */
        bufferCount = 0;

        /* Set the initial buffer. */
        buffer = entry->commandBuffer;

        /* Loop through all subbuffers. */
        while (buffer) {
            /* Update the count. */
            bufferCount += 1;

            /* Advance to the next subbuffer. */
            buffer = buffer->nextSubBuffer;
        }

#if !defined(gcvCOMMAND_BUFFER_NAME)
        if (bufferCount > 1) {
            gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND,
                           "  COMMAND BUFFER SET: %d buffers.\n",
                           bufferCount);
        }
#        endif

        /* Reset the buffer index. */
        bufferIndex = 0;

        /* Set the initial buffer. */
        buffer = entry->commandBuffer;

        /* Loop through all subbuffers. */
        while (buffer) {
            /* Determine the size of the buffer. */
            size = buffer->dataCount * Command->info.commandAlignment;

#if !defined(gcvCOMMAND_BUFFER_NAME)
            /* A single buffer? */
            if (bufferCount == 1) {
                gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND,
                               "  COMMAND BUFFER: count=%d (0x%X), size=%d bytes @ %08X.\n",
                               buffer->dataCount, buffer->dataCount, size, buffer->address);
            } else {
                gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND,
                               "  COMMAND BUFFER %d: count=%d (0x%X), size=%d bytes @ %08X\n",
                               bufferIndex, buffer->dataCount, buffer->dataCount, size, buffer->address);
            }
#        endif

            /* Determine the number of double words to print. */
            count = size / 4;

            /* Determine the buffer location. */
            data = (gctUINT32_PTR)((gctUINT8_PTR)buffer + buffer->bufferOffset);

#if defined(gcvCOMMAND_BUFFER_NAME)
            gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND,
                           "unsigned int _" gcvCOMMAND_BUFFER_NAME "_%d[] =\n", arrayCount);

            gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "{\n");

            arrayCount += 1;
#        endif

#if gcvDUMP_COMMAND_LINES
            /* Reset the line number. */
            lineNumber = 0;
#        endif

#if defined(gcvCOMMAND_BUFFER_NAME)
            count -= 2;
#        endif

            for (i = 0; i < count; i += 1) {
                if ((i % 8) == 0) {
#if defined(gcvCOMMAND_BUFFER_NAME)
                    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "\t");
#        else
                    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "    ");
#        endif
                }

#if gcvDUMP_COMMAND_LINES
                if (lineNumber == gcvDUMP_COMMAND_LINES) {
                    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND,
                                   " . . . . . . . . .\n");
                    break;
                }
#        endif
                gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "0x%08X", data[i]);

                if (i + 1 == count) {
                    gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "\n");

#if gcvDUMP_COMMAND_LINES
                    lineNumber += 1;
#        endif
                } else {
                    if (((i + 1) % 8) == 0) {
                        gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, ",\n");

#if gcvDUMP_COMMAND_LINES
                        lineNumber += 1;
#        endif
                    } else {
                        gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, ", ");
                    }
                }
            }

#if defined(gcvCOMMAND_BUFFER_NAME)
            gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "};\n\n");
#        endif

            /* Advance to the next subbuffer. */
            buffer = buffer->nextSubBuffer;
            bufferIndex += 1;
        }

        /* Advance to the next entry. */
        entry += 1;
    }
}
#    endif

static gceSTATUS
_LockCurrentQueue(IN gckVGCOMMAND Command,
                  OUT gcsKERNEL_CMDQUEUE_PTR *Entries,
                  OUT gctUINT_PTR EntryCount)
{
    gceSTATUS status;

    do {
        gcsKERNEL_QUEUE_HEADER_PTR queueHead;

        /* Get a shortcut to the head of the queue. */
        queueHead = Command->queueHead;

        /* Is the head buffer still being worked on? */
        if (queueHead->pending) {
            /* Increment overflow count. */
            Command->queueOverflow += 1;

            /* Wait until the head becomes idle. */
            gcmkERR_BREAK(_WaitForIdle(Command, queueHead));
        }

        /* Acquire the mutex. */
        gcmkERR_BREAK(gckOS_AcquireMutex(Command->os, Command->queueMutex, gcvINFINITE));

        /* Determine the first queue entry. */
        queueHead->currentEntry =
            (gcsKERNEL_CMDQUEUE_PTR)((gctUINT8_PTR)queueHead + gcmSIZEOF(gcsKERNEL_QUEUE_HEADER));

        /* Set the pointer to the first entry. */
        *Entries = queueHead->currentEntry;

        /* Determine the number of available entries. */
        *EntryCount = queueHead->size / gcmSIZEOF(gcsKERNEL_CMDQUEUE);

        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

static gceSTATUS
_UnlockCurrentQueue(IN gckVGCOMMAND Command, IN gctUINT EntryCount)
{
    gceSTATUS status;

    do {
#if !gcdENABLE_INFINITE_SPEED_HW
        gcsKERNEL_QUEUE_HEADER_PTR queueTail;
        gcsKERNEL_QUEUE_HEADER_PTR queueHead;
        gcsKERNEL_QUEUE_HEADER_PTR queueNext;
        gctUINT                    queueSize;
        gctUINT                    newSize;
        gctUINT                    unusedSize;

        /* Get shortcut to the head and to the tail of the queue. */
        queueTail = Command->queueTail;
        queueHead = Command->queueHead;

        /* Dump the command buffer. */
#if gcvDUMP_COMMAND_BUFFER
        _DumpCommandQueue(Command, queueHead, EntryCount);
#        endif

        /* Get a shortcut to the current queue size. */
        queueSize = queueHead->size;

        /* Determine the new queue size. */
        newSize = EntryCount * gcmSIZEOF(gcsKERNEL_CMDQUEUE);
        gcmkASSERT(newSize <= queueSize);

        /* Determine the size of the unused area. */
        unusedSize = queueSize - newSize;

        /* Is the unused area big enough to become a buffer? */
        if (unusedSize >= gcvMINUMUM_BUFFER) {
            gcsKERNEL_QUEUE_HEADER_PTR nextHead;

            /* Place the new header. */
            nextHead = (gcsKERNEL_QUEUE_HEADER_PTR)((gctUINT8_PTR)queueHead +
                                                    gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) + newSize);

            /* Initialize the buffer. */
            nextHead->size    = unusedSize - gcmSIZEOF(gcsKERNEL_QUEUE_HEADER);
            nextHead->pending = 0;

            /* Link the buffer in. */
            nextHead->next  = queueHead->next;
            queueHead->next = nextHead;
            queueNext       = nextHead;

            /* Update the size of the current buffer. */
            queueHead->size = newSize;
        } else { /* Not big enough. */
            /* Determine the next queue. */
            queueNext = queueHead->next;
        }

        /* Mark the buffer as busy. */
        queueHead->pending = EntryCount;

        /* Advance to the next buffer. */
        Command->queueHead = queueNext;

        /* Start the command processor if the queue was empty. */
        if (queueTail == queueHead) {
            gcsCMDBUFFER_PTR commandBuffer;

            /* The first entry must be a command buffer. */
            commandBuffer = queueTail->currentEntry->commandBuffer;

            /* Start the command processor. */
            gcmkERR_BREAK(gckVGHARDWARE_Execute(Command->hardware,
                                                commandBuffer->address,
                                                commandBuffer->dataCount));
        } else { /* The queue was not empty. */
            /* Advance the merge buffer if needed. */
            if (queueHead == Command->mergeQueue)
                Command->mergeQueue = queueNext;
        }
#    endif

        /* Release the mutex. */
        gcmkERR_BREAK(gckOS_ReleaseMutex(Command->os, Command->queueMutex));

        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    /* Return status. */
    return status;
}

/******************************************************************************
 ****************************** gckVGCOMMAND API Code *************************
 ******************************************************************************/
gceSTATUS
gckVGCOMMAND_Construct(IN gckVGKERNEL Kernel,
                       IN gctUINT TaskGranularity,
                       IN gctUINT QueueSize,
                       OUT gckVGCOMMAND *Command)
{
    gceSTATUS                  status, last;
    gckVGCOMMAND               command = gcvNULL;
    gcsKERNEL_QUEUE_HEADER_PTR queue;
    gctUINT                    i, j;

    gcmkHEADER_ARG("Kernel=%p TaskGranularity=0x%x QueueSize=0x%x Command=%p",
                   Kernel, TaskGranularity, QueueSize, Command);
    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
    gcmkVERIFY_ARGUMENT(QueueSize >= gcvMINUMUM_BUFFER);
    gcmkVERIFY_ARGUMENT(Command != gcvNULL);

    do {
        /***********************************************************************
         * Generic object initialization.
         */

        /* Allocate the gckVGCOMMAND structure. */
        gcmkERR_BREAK(gckOS_Allocate(Kernel->os,
                                     gcmSIZEOF(struct _gckVGCOMMAND),
                                     (gctPOINTER *)&command));

        /* Initialize the object. */
        command->object.type = gcvOBJ_COMMAND;

        /* Set the object pointers. */
        command->kernel   = Kernel;
        command->os       = Kernel->os;
        command->hardware = Kernel->hardware;

        /* Reset pointers. */
        command->queue       = gcvNULL;
        command->queueMutex  = gcvNULL;
        command->taskMutex   = gcvNULL;
        command->commitMutex = gcvNULL;

        command->powerStallBuffer = gcvNULL;
        command->powerStallSignal = gcvNULL;
        command->powerSemaphore   = gcvNULL;

        /* Reset context states. */
        command->contextCounter = 0;
        command->currentContext = 0;

        /* Enable command buffer dumping. */
        command->enableDumping = gcvTRUE;

        /* Set features. */
        command->fe20 = Kernel->hardware->fe20;
        command->vg20 = Kernel->hardware->vg20;
        command->vg21 = Kernel->hardware->vg21;

        /* Reset task table .*/
        gcmkVERIFY_OK(gckOS_ZeroMemory(command->taskTable, gcmSIZEOF(command->taskTable)));

        /* Query command buffer attributes. */
        gcmkERR_BREAK(gckVGCOMMAND_InitializeInfo(command));

        /* Create the control mutexes. */
        gcmkERR_BREAK(gckOS_CreateMutex(Kernel->os, &command->queueMutex));
        gcmkERR_BREAK(gckOS_CreateMutex(Kernel->os, &command->taskMutex));
        gcmkERR_BREAK(gckOS_CreateMutex(Kernel->os, &command->commitMutex));

        /* Create the power management semaphore. */
        gcmkERR_BREAK(gckOS_CreateSemaphore(Kernel->os, &command->powerSemaphore));

        gcmkERR_BREAK(gckOS_CreateSignal(Kernel->os, gcvFALSE, &command->powerStallSignal));

        /***********************************************************************
         * Command queue initialization.
         */

        /* Allocate the command queue. */
        gcmkERR_BREAK(gckOS_Allocate(Kernel->os, QueueSize, (gctPOINTER *)&command->queue));

        /* Initialize the command queue. */
        queue = command->queue;

        queue->size    = QueueSize - gcmSIZEOF(gcsKERNEL_QUEUE_HEADER);
        queue->pending = 0;
        queue->next    = queue;

        command->queueHead  = command->queue;
        command->queueTail  = command->queue;
        command->mergeQueue = command->queue;

        command->queueOverflow = 0;

        /***********************************************************************
         * Enable TS overflow interrupt.
         */

        command->info.tsOverflowInt = 0;
        gcmkERR_BREAK(gckVGINTERRUPT_Enable(Kernel->interrupt,
                                            &command->info.tsOverflowInt,
                                            _EventHandler_TSOverflow));

        /* Mask out the interrupt. */
        /* Kernel->hardware->eventMask &= ~(1 << command->info.tsOverflowInt); */

        /***********************************************************************
         * Enable Bus Error interrupt.
         */

        /* Hardwired to bit 31. */
        command->busErrorInt = 31;

        /* Enable the interrupt. */
        gcmkERR_BREAK(gckVGINTERRUPT_Enable(Kernel->interrupt,
                                            &command->busErrorInt,
                                            _EventHandler_BusError));

        command->powerStallInt = 30;
        /* Enable the interrupt. */
        gcmkERR_BREAK(gckVGINTERRUPT_Enable(Kernel->interrupt,
                                            &command->powerStallInt,
                                            _EventHandler_PowerStall));

        /***********************************************************************
         * Task management initialization.
         */

        command->taskStorage            = gcvNULL;
        command->taskStorageGranularity = TaskGranularity;
        command->taskStorageUsable      = TaskGranularity - gcmSIZEOF(gcsTASK_STORAGE);

        command->taskFreeHead = gcvNULL;
        command->taskFreeTail = gcvNULL;

        /* Enable block handlers. */
        for (i = 0; i < gcmCOUNTOF(_blockHandlers); i += 1) {
            /* Get the target hardware block. */
            gceBLOCK block = _blockHandlers[i].block;

            /* Get the interrupt array entry. */
            gcsBLOCK_TASK_ENTRY_PTR entry = &command->taskTable[block];

            /* Determine the interrupt value index. */
            gctUINT index = entry->interruptCount;

            /* Create the block semaphore. */
            if (entry->interruptSemaphore == gcvNULL)
                gcmkERR_BREAK(gckOS_CreateSemaphoreVG(command->os,
                                                      &entry->interruptSemaphore));

            /* Enable auto-detection. */
            entry->interruptArray[index] = -1;

            /* Enable interrupt for the block. */
            gcmkERR_BREAK(gckVGINTERRUPT_Enable(Kernel->interrupt,
                                                &entry->interruptArray[index],
                                                _blockHandlers[i].handler));

            /* Update the number of registered interrupts. */
            entry->interruptCount += 1;

            /* Inrement the semaphore to allow the usage of the registered
             * interrupt.
             */
            gcmkERR_BREAK(gckOS_IncrementSemaphore(command->os,
                                                   entry->interruptSemaphore));
        }

        /* Error? */
        if (gcmkIS_ERROR(status))
            break;

        /* Get the FE interrupt. */
        command->info.feBufferInt = command->taskTable[gcvBLOCK_COMMAND].interruptArray[0];

        /* Return gckVGCOMMAND object pointer. */
        *Command = command;

        gcmkFOOTER_ARG("*Command=%p", *Command);
        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    /* Roll back. */
    if (command != gcvNULL) {
        /* Disable block handlers. */
        for (i = 0; i < gcvBLOCK_COUNT; i += 1) {
            /* Get the task table entry. */
            gcsBLOCK_TASK_ENTRY_PTR entry = &command->taskTable[i];

            /* Destroy the semaphore. */
            if (entry->interruptSemaphore != gcvNULL)
                gcmkCHECK_STATUS(gckOS_DestroySemaphore(command->os,
                                                        entry->interruptSemaphore));

            /* Disable all enabled interrupts. */
            for (j = 0; j < entry->interruptCount; j += 1) {
                /* Must be a valid value. */
                gcmkASSERT(entry->interruptArray[j] >= 0);
                gcmkASSERT(entry->interruptArray[j] <= 31);

                /* Disable the interrupt. */
                gcmkCHECK_STATUS(gckVGINTERRUPT_Disable(Kernel->interrupt,
                                                        entry->interruptArray[j]));
            }
        }

        /* Disable the bus error interrupt. */
        gcmkCHECK_STATUS(gckVGINTERRUPT_Disable(Kernel->interrupt, command->busErrorInt));

        /* Disable TS overflow interrupt. */
        if (command->info.tsOverflowInt != -1) {
            gcmkCHECK_STATUS(gckVGINTERRUPT_Disable(Kernel->interrupt,
                                                    command->info.tsOverflowInt));
        }

        /* Delete the commit mutex. */
        if (command->commitMutex != gcvNULL)
            gcmkCHECK_STATUS(gckOS_DeleteMutex(Kernel->os, command->commitMutex));

        /* Delete the command queue mutex. */
        if (command->taskMutex != gcvNULL)
            gcmkCHECK_STATUS(gckOS_DeleteMutex(Kernel->os, command->taskMutex));

        /* Delete the command queue mutex. */
        if (command->queueMutex != gcvNULL)
            gcmkCHECK_STATUS(gckOS_DeleteMutex(Kernel->os, command->queueMutex));

        /* Delete the command queue. */
        if (command->queue != gcvNULL)
            gcmkCHECK_STATUS(gckOS_Free(Kernel->os, command->queue));

        if (command->powerSemaphore != gcvNULL)
            gcmkVERIFY_OK(gckOS_DestroySemaphore(Kernel->os, command->powerSemaphore));

        if (command->powerStallSignal != gcvNULL) {
            /* Create the power management semaphore. */
            gcmkVERIFY_OK(gckOS_DestroySignal(Kernel->os, command->powerStallSignal));
        }

        /* Free the gckVGCOMMAND structure. */
        gcmkCHECK_STATUS(gckOS_Free(Kernel->os, command));
    }

    gcmkFOOTER();
    /* Return the error. */
    return status;
}

gceSTATUS
gckVGCOMMAND_Destroy(OUT gckVGCOMMAND Command)
{
    gceSTATUS status = gcvSTATUS_OK;

    gcmkHEADER_ARG("Command=%p", Command);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);

    do {
        gctUINT             i;
        gcsTASK_STORAGE_PTR nextStorage;

        if (Command->queueHead != gcvNULL) {
            /* Wait until the head becomes idle. */
            gcmkERR_BREAK(_WaitForIdle(Command, Command->queueHead));
        }

        /* Disable block handlers. */
        for (i = 0; i < gcvBLOCK_COUNT; i += 1) {
            /* Get the interrupt array entry. */
            gcsBLOCK_TASK_ENTRY_PTR entry = &Command->taskTable[i];

            /* Determine the index of the last interrupt in the array. */
            gctINT index = entry->interruptCount - 1;

            /* Destroy the semaphore. */
            if (entry->interruptSemaphore != gcvNULL)
                gcmkERR_BREAK(gckOS_DestroySemaphore(Command->os, entry->interruptSemaphore));

            /* Disable all enabled interrupts. */
            while (index >= 0) {
                /* Must be a valid value. */
                gcmkASSERT(entry->interruptArray[index] >= 0);
                gcmkASSERT(entry->interruptArray[index] <= 31);

                /* Disable the interrupt. */
                gcmkERR_BREAK(gckVGINTERRUPT_Disable(Command->kernel->interrupt,
                                                     entry->interruptArray[index]));

                /* Update to the next interrupt. */
                index -= 1;
                entry->interruptCount -= 1;
            }

            /* Error? */
            if (gcmkIS_ERROR(status))
                break;
        }

        /* Error? */
        if (gcmkIS_ERROR(status))
            break;

        /* Disable the bus error interrupt. */
        gcmkERR_BREAK(gckVGINTERRUPT_Disable(Command->kernel->interrupt,
                                             Command->busErrorInt));

        /* Disable TS overflow interrupt. */
        if (Command->info.tsOverflowInt != -1) {
            gcmkERR_BREAK(gckVGINTERRUPT_Disable(Command->kernel->interrupt,
                                                 Command->info.tsOverflowInt));

            Command->info.tsOverflowInt = -1;
        }

        /* Delete the commit mutex. */
        if (Command->commitMutex != gcvNULL) {
            gcmkERR_BREAK(gckOS_DeleteMutex(Command->os, Command->commitMutex));

            Command->commitMutex = gcvNULL;
        }

        /* Delete the command queue mutex. */
        if (Command->taskMutex != gcvNULL) {
            gcmkERR_BREAK(gckOS_DeleteMutex(Command->os, Command->taskMutex));

            Command->taskMutex = gcvNULL;
        }

        /* Delete the command queue mutex. */
        if (Command->queueMutex != gcvNULL) {
            gcmkERR_BREAK(gckOS_DeleteMutex(Command->os, Command->queueMutex));

            Command->queueMutex = gcvNULL;
        }

        if (Command->powerSemaphore != gcvNULL) {
            /* Destroy the power management semaphore. */
            gcmkERR_BREAK(gckOS_DestroySemaphore(Command->os, Command->powerSemaphore));
        }

        if (Command->powerStallSignal != gcvNULL) {
            /* Create the power management semaphore. */
            gcmkERR_BREAK(gckOS_DestroySignal(Command->os, Command->powerStallSignal));
        }

        if (Command->queue != gcvNULL) {
            /* Delete the command queue. */
            gcmkERR_BREAK(gckOS_Free(Command->os, Command->queue));
        }

        /* Destroy all allocated buffers. */
        while (Command->taskStorage) {
            /* Copy the buffer pointer. */
            nextStorage = Command->taskStorage->next;

            /* Free the current container. */
            gcmkERR_BREAK(gckOS_Free(Command->os, Command->taskStorage));

            /* Advance to the next one. */
            Command->taskStorage = nextStorage;
        }

        /* Error? */
        if (gcmkIS_ERROR(status))
            break;

        /* Mark the object as unknown. */
        Command->object.type = gcvOBJ_UNKNOWN;

        /* Free the gckVGCOMMAND structure. */
        gcmkERR_BREAK(gckOS_Free(Command->os, Command));

        gcmkFOOTER_NO();
        /* Success. */
        return gcvSTATUS_OK;
    } while (gcvFALSE);

    /* Restore the object type if failed. */
    Command->object.type = gcvOBJ_COMMAND;

    gcmkFOOTER();
    /* Return the error. */
    return status;
}

gceSTATUS
gckVGCOMMAND_QueryCommandBuffer(IN gckVGCOMMAND Command,
                                OUT gcsCOMMAND_BUFFER_INFO_PTR Information)
{
    gcmkHEADER_ARG("Command=%p Information=%p", Command, Information);
    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
    gcmkVERIFY_ARGUMENT(Information != gcvNULL);

    /* Copy the information. */
    gcmkVERIFY_OK(gckOS_MemCopy(Information, &Command->info,
                                sizeof(gcsCOMMAND_BUFFER_INFO)));

    gcmkFOOTER_NO();
    /* Success. */
    return gcvSTATUS_OK;
}

gceSTATUS
gckVGCOMMAND_Allocate(IN gckVGCOMMAND Command, IN gctSIZE_T Size,
                      OUT gcsCMDBUFFER_PTR *CommandBuffer, OUT gctPOINTER *Data)
{
    gceSTATUS status;

    gcmkHEADER_ARG("Command=%p Size=0x%x CommandBuffer=%p Data=%p",
                   Command, Size, CommandBuffer, Data);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
    gcmkVERIFY_ARGUMENT(Data != gcvNULL);

    do {
        /* Allocate the buffer. */
        gcmkERR_BREAK(_AllocateCommandBuffer(Command, Size, CommandBuffer));

        /* Determine the data pointer. */
        *Data = (gctUINT8_PTR)(*CommandBuffer) + (*CommandBuffer)->bufferOffset;
    } while (gcvFALSE);

    gcmkFOOTER();
    /* Return status. */
    return status;
}

gceSTATUS
gckVGCOMMAND_Free(IN gckVGCOMMAND Command, IN gcsCMDBUFFER_PTR CommandBuffer)
{
    gceSTATUS status;

    gcmkHEADER_ARG("Command=%p CommandBuffer=%p", Command, CommandBuffer);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
    gcmkVERIFY_ARGUMENT(CommandBuffer != gcvNULL);

    /* Free command buffer. */
    status = _FreeCommandBuffer(Command->kernel, CommandBuffer);

    gcmkFOOTER();
    /* Return status. */
    return status;
}

gceSTATUS
gckVGCOMMAND_Execute(IN gckVGCOMMAND Command, IN gcsCMDBUFFER_PTR CommandBuffer)
{
    gceSTATUS status;

    gcmkHEADER_ARG("Command=%p CommandBuffer=%p", Command, CommandBuffer);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
    gcmkVERIFY_ARGUMENT(CommandBuffer != gcvNULL);

    do {
        gctUINT                queueLength;
        gcsKERNEL_CMDQUEUE_PTR kernelEntry;

        /* Lock the current queue. */
        gcmkERR_BREAK(_LockCurrentQueue(Command, &kernelEntry, &queueLength));

        /* Set the buffer. */
        kernelEntry->commandBuffer = CommandBuffer;
        kernelEntry->handler       = _FreeKernelCommandBuffer;

        /* Lock the current queue. */
        gcmkERR_BREAK(_UnlockCurrentQueue(Command, 1));
    } while (gcvFALSE);

    gcmkFOOTER();
    /* Return status. */
    return status;
}

gceSTATUS
gckVGCOMMAND_Commit(IN gckVGCOMMAND Command, IN gcsVGCONTEXT_PTR Context,
                    IN gcsVGCMDQUEUE_PTR Queue, IN gctUINT EntryCount,
                    IN gcsTASK_MASTER_TABLE_PTR TaskTable)
{
    /*
     *  The first buffer is executed through a direct gckVGHARDWARE_Execute call,
     *  therefore only an update is needed after the execution is over. All
     *  consequent buffers need to be executed upon the first update call from
     *  the FE interrupt handler.
     */
    static gcsQUEUE_UPDATE_CONTROL _dynamicBuffer[] = {
        {
            _UpdateDynamicCommandBuffer,
            _UpdateDynamicCommandBuffer,
            _UpdateLastDynamicCommandBuffer,
            _UpdateLastDynamicCommandBuffer
        },
        {
            _ExecuteDynamicCommandBuffer,
            _UpdateDynamicCommandBuffer,
            _ExecuteLastDynamicCommandBuffer,
            _UpdateLastDynamicCommandBuffer
        }
    };

    static gcsQUEUE_UPDATE_CONTROL _staticBuffer[] = {
        { _UpdateStaticCommandBuffer, _UpdateStaticCommandBuffer, _UpdateLastStaticCommandBuffer,
          _UpdateLastStaticCommandBuffer },
        { _ExecuteStaticCommandBuffer, _UpdateStaticCommandBuffer, _ExecuteLastStaticCommandBuffer,
          _UpdateLastStaticCommandBuffer }
    };

    gceSTATUS                    status;
    struct _gcsTASK_MASTER_TABLE _TaskTable;
#ifdef __QNXNTO__
    gcsVGCONTEXT_PTR         userContext         = gcvNULL;
    gctBOOL                  userContextMapped   = gcvFALSE;
    gcsTASK_MASTER_TABLE_PTR userTaskTable       = gcvNULL;
    gctBOOL                  userTaskTableMapped = gcvFALSE;
    gctPOINTER               pointer             = gcvNULL;
#    endif
    struct _gcsVGCONTEXT _Context;
    gctBOOL              needCopy = gcvFALSE;

    gctBOOL                     haveFETasks;
    gctUINT                     queueSize   = 0;
    gcsVGCMDQUEUE_PTR           mappedQueue = gcvNULL;
    gcsVGCMDQUEUE_PTR           userEntry   = gcvNULL;
    gcsKERNEL_CMDQUEUE_PTR      kernelEntry;
    gcsQUEUE_UPDATE_CONTROL_PTR queueControl;
    gctUINT                     currentLength;
    gctUINT                     queueLength;
    gctUINT                     entriesQueued;
    gctUINT8_PTR                previousEnd;
    gctBOOL                     previousDynamic;
    gctBOOL                     previousExecuted;
    gctUINT                     controlIndex;
    gctINT                      pid;
    gctBOOL                     powerSemaphoreAcquired = gcvFALSE;
    gctBOOL                     commitMutexAcquired    = gcvFALSE;
    gctBOOL                     queueMutexAcquired     = gcvFALSE;

    gcmkHEADER_ARG("Command=%p Context=%p Queue=%p EntryCount=0x%x TaskTable=%p",
                   Command, Context, Queue, EntryCount, TaskTable);

    /* Verify the arguments. */
    gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND);
    gcmkVERIFY_ARGUMENT(Context != gcvNULL);
    gcmkVERIFY_ARGUMENT(Queue != gcvNULL);
    gcmkVERIFY_ARGUMENT(EntryCount > 1);

#ifdef __QNXNTO__
    /* Map the context into the kernel space. */
    userContext = Context;

    gcmkONERROR(gckOS_MapUserPointer(Command->os, userContext,
                                     gcmSIZEOF(*userContext), &pointer));

    Context = pointer;

    userContextMapped = gcvTRUE;

    /* Map the taskTable into the kernel space. */
    userTaskTable = TaskTable;

    gcmkONERROR(gckOS_MapUserPointer(Command->os, userTaskTable,
                                     gcmSIZEOF(*userTaskTable), &pointer));

    TaskTable = pointer;

    userTaskTableMapped = gcvTRUE;

    /* Update the signal info. */
    TaskTable->rcvid = Context->rcvid;
    TaskTable->event = Context->event;
#    endif

    gcmkONERROR(gckOS_GetProcessID((gctUINT32_PTR)&pid));
    gcmkONERROR(gckOS_QueryNeedCopy(Command->os, pid, &needCopy));
    if (needCopy) {
        gcmkONERROR(gckOS_CopyFromUserData(Command->os, &_TaskTable, TaskTable,
                                           gcmSIZEOF(struct _gcsTASK_MASTER_TABLE)));
        TaskTable = &_TaskTable;
        /* Determine whether there are FE tasks to be performed. */
        gcmkONERROR(gckOS_CopyFromUserData(Command->os, &_Context, Context,
                                           gcmSIZEOF(struct _gcsVGCONTEXT)));
        Context = &_Context;
    }

    gcmkONERROR(gckVGHARDWARE_SetPowerState(Command->hardware, gcvPOWER_ON_AUTO));

    /* Acquire the power semaphore. */
    gcmkONERROR(gckOS_AcquireSemaphore(Command->os, Command->powerSemaphore));
    powerSemaphoreAcquired = gcvTRUE;
    /* Acquire the mutex. */
    gcmkONERROR(gckOS_AcquireMutex(Command->os, Command->commitMutex, gcvINFINITE));
    commitMutexAcquired = gcvTRUE;

    gcmkONERROR(_FlushMMU(Command));

    /* Assign a context ID if not yet assigned. */
    if (Context->id == 0) {
        /* Assign the next context number. */
        Context->id = ++Command->contextCounter;
        /* See if we overflowed. */
        if (Command->contextCounter == 0) {
            /* We actually did overflow, wow... */
            gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES);
        }
    }

    /* The first entry in the queue is always the context buffer.
     * Verify whether the user context is the same as the current
     * context and if that's the case, skip the first entry.
     */
    if (Context->id == Command->currentContext) {
        /* Same context as before, skip the first entry. */
        EntryCount -= 1;
        Queue += 1;

        /* Set the signal to avoid user waiting. */
#ifdef __QNXNTO__
        gckOS_UserSignal(Command->os, Context->userSignal, Context->rcvid, &Context->event);
#    else
        gcmkONERROR(gckOS_UserSignal(Command->os, Context->signal, Context->process));
#    endif
    } else {
        /* Different user context - keep the first entry.
         * Set the user context as the current one.
         */
        Command->currentContext = Context->id;
    }

    /* Reset pointers. */
    queueControl = gcvNULL;
    previousEnd  = gcvNULL;

    haveFETasks = (TaskTable->table[gcvBLOCK_COMMAND].head != gcvNULL);

    /* Determine the size of the queue. */
    queueSize = EntryCount * gcmSIZEOF(gcsVGCMDQUEUE);
    if (needCopy) {
        gctPOINTER pointer = gcvNULL;

        gcmkONERROR(gckOS_Allocate(Command->os, queueSize, &pointer));
        userEntry   = pointer;
        mappedQueue = pointer;
        gcmkONERROR(gckOS_CopyFromUserData(Command->os, userEntry, Queue, queueSize));
    } else {
        /* Map the command queue into the kernel space. */
        gcmkONERROR(gckOS_MapUserPointer(Command->os, Queue, queueSize,
                                         (gctPOINTER *)&mappedQueue));
        userEntry = mappedQueue;
    }
    /* Set the first entry. */

    /* Process the command queue. */
    while (EntryCount) {
        /* Lock the current queue. */
        gcmkONERROR(_LockCurrentQueue(Command, &kernelEntry, &queueLength));
        queueMutexAcquired = gcvTRUE;

        /* Determine the number of entries to process. */
        currentLength = (queueLength < EntryCount) ? queueLength : EntryCount;

        /* Update the number of the entries left to process. */
        EntryCount -= currentLength;

        /* Reset previous flags. */
        previousDynamic  = gcvFALSE;
        previousExecuted = gcvFALSE;

        /* Set the initial control index. */
        controlIndex = 0;

        /* Process entries. */
        for (entriesQueued = 0; entriesQueued < currentLength; entriesQueued += 1) {
            /* Get the kernel pointer to the command buffer header. */
            gcsCMDBUFFER_PTR commandBuffer = gcvNULL;

            gcmkONERROR(_ConvertUserCommandBufferPointer(Command,
                                                         userEntry->commandBuffer,
                                                         &commandBuffer));

            /* Is it a dynamic command buffer? */
            if (userEntry->dynamic) {
                /* Select dynamic buffer control functions. */
                queueControl = &_dynamicBuffer[controlIndex];
            } else { /* No, a static command buffer. */
                /* Select static buffer control functions. */
                queueControl = &_staticBuffer[controlIndex];
            }

            /* Set the command buffer pointer to the entry. */
            kernelEntry->commandBuffer = commandBuffer;

            /* If the previous entry was a dynamic command buffer,
             * link it to the current.
             */
            if (previousDynamic) {
                gcmkONERROR(gckVGCOMMAND_FetchCommand(Command, previousEnd,
                                                      commandBuffer->address,
                                                      commandBuffer->dataCount, gcvNULL));

                /* The buffer will be auto-executed, only need to
                 * update it after it has been executed.
                 */
                kernelEntry->handler = queueControl->update;

                /* The buffer is only being updated. */
                previousExecuted = gcvFALSE;
            } else {
                /* Set the buffer up for execution. */
                kernelEntry->handler = queueControl->execute;

                /* The buffer is being updated. */
                previousExecuted = gcvTRUE;
            }

            /* The current buffer's END command becomes the last END. */
            previousEnd = ((gctUINT8_PTR)commandBuffer) + commandBuffer->bufferOffset +
                          commandBuffer->dataCount * Command->info.commandAlignment -
                          Command->info.staticTailSize;

            /* Update the last entry info. */
            previousDynamic = userEntry->dynamic;

            /* Advance entries. */
            userEntry++;
            kernelEntry++;

            /* Update the control index. */
            controlIndex = 1;
        }

        /* If the previous entry was a dynamic command buffer,
         * terminate it with an END.
         */
        if (previousDynamic) {
            gcmkONERROR(gckVGCOMMAND_EndCommand(Command, previousEnd,
                                                Command->info.feBufferInt, gcvNULL));
        }

        /* Last buffer? */
        if (EntryCount == 0) {
            /*  Modify the last command buffer's routines to handle
             *  tasks if any.
             */
            if (haveFETasks) {
                if (previousExecuted)
                    kernelEntry[-1].handler = queueControl->lastExecute;
                else
                    kernelEntry[-1].handler = queueControl->lastUpdate;
            }

            /* Release the mutex. */
            gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->queueMutex));
            queueMutexAcquired = gcvFALSE;
            /* Add a semaphore stall after PE EVENT (Before the END). */
            {
                gctUINT32_PTR memory = (gctUINT32_PTR)(previousEnd - 0x10);
                *memory++            = 0x10000007;
                *memory++            = 0;

                *memory++ = 0x20000007;
                *memory++ = 0;
            }
            /* Schedule tasks. */
            gcmkONERROR(_ScheduleTasks(Command, TaskTable, previousEnd - 0x10));

            /* Acquire the mutex. */
            gcmkONERROR(gckOS_AcquireMutex(Command->os, Command->queueMutex, gcvINFINITE));
            queueMutexAcquired = gcvTRUE;
        }

        /* Unkock and schedule the current queue for execution. */
        gcmkONERROR(_UnlockCurrentQueue(Command, currentLength));
        queueMutexAcquired = gcvFALSE;
    }

OnError:
    if (queueMutexAcquired)
        gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->queueMutex));

    if (mappedQueue) {
        if (!needCopy) {
            /* Unmap the user command buffer. */
            gcmkVERIFY_OK(gckOS_UnmapUserPointer(Command->os, Queue, queueSize, mappedQueue));
        } else {
            gcmkVERIFY_OK(gckOS_Free(Command->os, mappedQueue));
        }
    }
    /* Release the mutex. */
    if (commitMutexAcquired)
        gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->commitMutex));

    if (powerSemaphoreAcquired)
        gcmkVERIFY_OK(gckOS_ReleaseSemaphore(Command->os, Command->powerSemaphore));

#ifdef __QNXNTO__
    if (userContextMapped) {
        /* Unmap the user context. */
        gcmkVERIFY_OK(gckOS_UnmapUserPointer(Command->os, userContext,
                                             gcmSIZEOF(*userContext), Context));
    }

    if (userTaskTableMapped) {
        /* Unmap the user taskTable. */
        gcmkVERIFY_OK(gckOS_UnmapUserPointer(Command->os, userTaskTable,
                                             gcmSIZEOF(*userTaskTable), TaskTable));
    }
#    endif

    gcmkFOOTER();
    /* Return status. */
    return status;
}

#endif /* gcdENABLE_VG */
